<?php


namespace app\common\business;


use app\common\lib\Redis;
use app\common\lib\ZhyxBeanstalkd;
use app\common\lib\ZhyxMongo;
use app\common\model\BaseModel;
use app\common\model\BindDiseasePath;
use app\common\model\Disease;
use app\common\model\DiseasePathBind;
use app\common\model\DiseasePatientTask;
use app\common\model\DiseasePatientTaskDetail;
use app\common\model\DiseaseTask;
use app\common\model\TaskDetail;
use catcher\exceptions\CatchException;
use catcher\exceptions\FailedException;
use catcher\exceptions\ValidateFailedException;
use MongoDB\BSON\ObjectId;
use Pheanstalk\Exception\JobNotFoundException;
use Pheanstalk\Exception\ServerException;
use Pheanstalk\Job;
use think\facade\Log;

class DiseasePath
{
    public $model = null;

    public function __construct()
    {
        $this->model = new \app\common\model\DiseasePath();
    }

    public function createData(array $data)
    {
        $data["deleted_at"] = 0;
        $this->judgmentData($data);
        //循环遍历内容 判断是否存在空值项目
        return $this->model->createBy($data);
    }

    private function judgmentData(array $data): void
    {
        foreach ($data["steps"] as $k => $v) {
            foreach ($v["step_detail"] as $kk => $vv) {
                if (in_array($vv["task_type"], ["content", "form"]) && !isset($vv["attachments"])) {
                    $errorMessage = sprintf("随访路径操作失败,%s节点中子序号为%s小类中缺少关联项目，请完善后在次提交", ($k + 1), ($kk + 1));
                    throw new ValidateFailedException($errorMessage);

                }
            }
        }
    }


    public function updateData(array $data, string $id): bool
    {
        //根据id判断是否存在
        $isExists = $this->model->find($id);
        if (empty($isExists)) {
            throw new FailedException("慢病随访路径不存在");
        }
        $this->judgmentData($data);
        return $isExists->save($data);
    }

    public function calcPath($patientInfo, string $bindId, $session = null, $customerDate = '', $isWriteDetail = true): array
    {

        $taskDetail = (new DiseaseTask())->getDiseaseTaskByBindId($bindId, $session);

        if (!empty($taskDetail)) {
            # 计算最终的日期
            $taskDetail["bind_main_id"] = $patientInfo["bind_main_id"] ?? "";
            $taskDetail["patient_name"] = $patientInfo["name"] ?? "";
            $pushDate = $this->calcPushDate($patientInfo["latest_hospital"], $taskDetail, $customerDate);
            $pushArr = [];
            foreach ($pushDate as $k => $v) {
                # 根据计算出来的日期和状态写入到任务表中
                $pushArr[] = $this->writeStepTask($taskDetail, $bindId, $v, $session, $isWriteDetail);
            }
            return $pushArr;
        }
        return [];
    }

    /**
     * @param $timeLine
     * @param $step
     * @return array|false|string
     */
    private function calcPushDate($timeLine, $step, $customerDate = '')
    {
        $status = 0; #状态  -1 未开启 0待进行 1进行中 2已完成 3已过期
        $plan_date = "";
        if (empty($customerDate)) {
            switch ($step["step_type"]["value"]) {
                case "in_hospital":
                    $date = $timeLine["in_hospital_date"] ?? "";
                    break;
                case "YZ":
                    $date = $timeLine["yz_date"] ?? "";
                    break;
                case "surgery":
                    $date = $timeLine["surgery_date"] ?? "";
                    break;
                case "leave_hospital":
                    $date = $timeLine["leave_hospital_date"] ?? "";
                    break;
                default:
                    $date = "";
                    break;
            }
        } else {

            $date = $customerDate;
        }

        # 判断日期是否为空，如果不为空说明有这个步骤的节点
        if (!empty($date)) {
            if ($step["step_is_repeat"]) {
                $calcDate = date_create_from_format("Y-m-d", $date)
                    ->modify("+{$step['step_start_value']} {$step['step_start_type']}")
                    ->format("Y-m-d");
                # 计算推送日期
                $calcTimestamp = strtotime($calcDate);

                #计算结束周期日期
                $finishDate = date_create_from_format("Y-m-d", $date)
                    ->modify("+{$step['step_repeat_finish_value']} {$step['step_repeat_finish_type']}")
                    ->format("Y-m-d");

                if ($calcDate > $finishDate) {
                    $status = 3;
                } else {
                    $pushData = [];
                    while ($calcTimestamp < strtotime($finishDate)) {

                        $tempTimeStamp = strtotime("+" . (int)$step["step_repeat_value"] . ' ' . $step["step_repeat_type"], strtotime(date("Y-m-d", $calcTimestamp)));

                        if ($tempTimeStamp <= strtotime($finishDate)) {

                            $calcTimestamp = $tempTimeStamp;
                            if ($calcTimestamp < time()) {
                                $pushData[] = [
                                    'status' => 3,
                                    'plan_date' => date("Y-m-d", $calcTimestamp)
                                ];
                            } else {
                                $pushData[] = [
                                    'status' => 0,
                                    'plan_date' => date("Y-m-d", $calcTimestamp)
                                ];
                            }

                        } else {
                            break;
                        }

                    }
                    return $pushData;

                }
            } else {
                $calcDate = strtotime("+" . (int)$step["step_start_value"] . ' ' . $step["step_start_type"], strtotime($date));
                //$calcDate = strtotime('+12 hours', $calcDate);  // 将当前时间戳加上12个小时
                $calcDate = strtotime(date("Y-m-d", $calcDate) . " 23:59:59");
                # 判断如果比今天小则代表结束了
                $status = $calcDate < time() ? 3 : 0;
                $plan_date = date("Y-m-d", $calcDate);
            }
        }

        return [[
            "status" => $status,
            "plan_date" => $plan_date
        ]];
    }


    private function writeStepTask($taskDetail, string $bindId, $pushDate, $session = null, $isWriteDetail = true): array
    {
        $planTaskId = new ObjectId();
        $connection = ZhyxMongo::getInstance()->setCollection('disease_patient_task');
        $pushData = [
            'plan_task_id' => $planTaskId,
            'plan_task_status' => $pushDate["status"],
            'plan_date' => $pushDate["plan_date"],
            'plan_title' => $taskDetail["step_task_title"],
            'complete_date' => '',
            'step_order' => $taskDetail['step_order']
        ];


        if (($taskDetail["planList"][0]["plan_date"] === "" || $taskDetail["planList"][0]["plan_date"] === $pushDate['plan_date']) && $isWriteDetail === false) {
            $result = ZhyxMongo::getInstance()->setCollection('disease_patient_task_detail')->deleteMany(["task_id" => new ObjectId($taskDetail["planList"][0]["plan_task_id"])], ['session' => $session]);
            $stepTaskResult = ZhyxMongo::getInstance()->setCollection('disease_patient_task')->updateOne(["_id" => new ObjectId($bindId)], ['$set' => [
                'planList' => [$pushData]
            ]], ["session" => $session]);
        } else {
            $stepTaskResult = ZhyxMongo::getInstance()->setCollection('disease_patient_task')->updateOne(["_id" => new ObjectId($bindId)], ['$push' => [
                'planList' => $pushData
            ]], ["session" => $session]);
        }
        if ($stepTaskResult->getModifiedCount() > 0) {
            $details = [];
            foreach ($taskDetail["step_detail"] as $dk => $dv) {
                $data = [
                    'task_name' => $dv["task_name"],
                    'task_type' => $dv["task_type"],
                    'latest_status' => $pushDate['status'],
                    'push_type' => $dv["push_type"] ?? "",
                    'outer_id' => '',
                    'task_start_date' => $pushDate['plan_date'],
                    'attachment_value' => '',
                    'attachment_title' => '',
                    'created_at' => time(),
                    'updated_at' => '',
                    'task_id' => $planTaskId,
                    'step_order' => $taskDetail["step_order"],
                    'patient_id' => $taskDetail["patient_id"],
                    'task_main_id' => $bindId,
                    'deleted_at' => 0,
                    'sms_id' => $dv["sms_id"] ?? 0,
                    'bind_main_id' => $taskDetail["bind_main_id"] ?? "",
                    'patient_name' => $taskDetail["patient_name"] ?? ""
                ];
                if (isset($dv["attachments"])) {
                    $attachments = iterator_to_array($dv["attachments"]);
                } else {
                    $attachments = [];
                }
                if (isset($dv["attachments"]) && !empty($attachments)) {
                    foreach ($dv["attachments"] as $ak => $av) {
                        $data["attachment_value"] = $av["attachment_value"];
                        $data["attachment_title"] = $av["attachment_title"];
                        $data["attachment_parent_value"] = $av["attachment_parent_value"] ?? "";
                        $data["attachment_parent_title"] = $av["attachment_parent_title"] ?? "";
                        $details[] = $data;
                    }
                } else {
                    $details[] = $data;
                }
            }
            $taskDetail = ZhyxMongo::getInstance()->setCollection('disease_patient_task_detail')->insertMany($details, ["session" => $session]);
            if ($taskDetail->getInsertedCount() === 0) {
                throw new FailedException("任务入库失败");
            }

            return $pushData;
        }
        return [];
    }

    /**
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \JsonException
     */
    public function pushTask(array $tasks): bool
    {


        foreach ($tasks as $k => $v) {

            $pushDate = strtotime($v["plan_date"] . env("push_time.time", " 09:00"));
            $delayTime = $pushDate - time();
            if ($delayTime < 0) {
                $delayTime = 0;
            }
            if ($delayTime < 0) {
                $delayTime = 0;
            }

            /*  if ($v["plan_task_status"] == 0 || $v["plan_task_status"] == 1) {
                  $taskLists = (new DiseasePatientTaskDetail())->where([["task_id", "=", $v["plan_task_id"]]])->select();
                  if (!empty($taskLists)) {
                      $v["tubeName"] = "TagDiseaseTask";
                      $taskResult = ZhyxBeanstalkd::getInstance()->beansTalk->useTube("tagDiseaseTask")->put(json_encode($v, JSON_THROW_ON_ERROR), 0, $delayTime);
                      //Redis::getInstance()->redis->lpush($v["bind_id"], [$taskResult->getId()]);
                      Redis::getInstance()->redis->zadd($v["bind_id"], [$taskResult->getId() => $v["step_order"]]);

                      foreach ($taskLists as $tk => $tv) {
                          $tv["tubeName"] = "PushDiseaseTask";
                          $taskResult = ZhyxBeanstalkd::getInstance()->beansTalk->useTube("pushDiseaseTask")->put(json_encode($tv, JSON_THROW_ON_ERROR), 0, $delayTime);
                          Redis::getInstance()->redis->zadd($v["bind_id"], [$taskResult->getId() => $tv["step_order"]]);
                      }
                  }
              }*/
        }
        return true;
    }

    public function closeCase(string $diseasePathId, string $content): bool
    {
        try {
            $result = (new BindDiseasePath())->where("id", "=", $diseasePathId)->save([
                'is_close' => 1,
                'close_date' => date("Y-m-d H:i:s"),
                'conclusion' => $content
            ]);

            if ($result !== false) {

                $taskIdArr = (new DiseasePatientTask())->where("bind_id", "=", new ObjectId($diseasePathId))->where("planList.plan_task_status", "=", 0)->column('id');

                if (!empty($taskIdArr)) {
                    (new DiseasePatientTask())->where("id", "in", $taskIdArr)->where("planList.plan_task_status", "=", 0)->save([
                        'planList.$.plan_task_status' => 4
                    ]);
                    (new DiseasePatientTaskDetail())->where("task_main_id", "in", $taskIdArr)->where("latest_status", "=", 0)->save([
                        'latest_status' => 4
                    ]);
                }

                # 将beanstalkd任务进行删除操作
                $jobIdCount = Redis::getInstance()->redis->zcard($diseasePathId);

                if ($jobIdCount > 0) {
                    for ($i = 0; $i < $jobIdCount; $i++) {
                        $jobId = Redis::getInstance()->redis->zrange($diseasePathId, 0, 0);
                        # 移除
                        Redis::getInstance()->redis->zrem($diseasePathId, $jobId[0]);
                        $beanJobId = new Job($jobId[0], '');
                        $job = ZhyxBeanstalkd::getInstance()->beansTalk->statsJob($beanJobId);
                        if ($job !== false) {
                            ZhyxBeanstalkd::getInstance()->beansTalk->delete($beanJobId);
                        }
                    }
                }
                return true;
            }
            return false;
        } catch (ServerException $serverException) {
            Log::error('任务已经消费:' . $serverException->getMessage());
        } catch (\Exception $exception) {
            throw new FailedException($exception->getMessage());
        }
        return true;

    }

    public function setStartPoint(string $diseasePathId, int $point): bool
    {
        try {
            //获取随访路径信息
            $diseasePathInfo = (new BindDiseasePath())->where("id", "=", $diseasePathId)->find();
            if (empty($diseasePathInfo)) {
                throw new FailedException("慢病随访路径不存在");
            }
            if (isset($diseasePathInfo["is_close"]) && $diseasePathInfo["is_close"] === 1) {
                throw new FailedException("此方案已经结案，不能再次设置起始节点");
            }
            $result = (new BindDiseasePath())->where("id", "=", $diseasePathId)->save([
                'step' => $point
            ]);
            if ($result !== false) {
                $taskIdArr = (new DiseasePatientTask())->where("bind_id", "=", new ObjectId($diseasePathId))->where("planList.plan_task_status", "=", 0)->where("step_order", "<", $point)->column('id');

                if (!empty($taskIdArr)) {
                    (new DiseasePatientTask())->where("id", "in", $taskIdArr)->where("planList.plan_task_status", "=", 0)->save([
                        'planList.$.plan_task_status' => 4
                    ]);
                    (new DiseasePatientTaskDetail())->where("task_main_id", "in", $taskIdArr)->where("latest_status", "=", 0)->save([
                        'latest_status' => 4
                    ]);

                }
                $result = Redis::getInstance()->redis->zrangebyscore($diseasePathId, 0, ($point - 1));
                if (!empty($result)) {
                    foreach ($result as $k => $v) {
                        # 移除
                        Redis::getInstance()->redis->zrem($diseasePathId, $v);
                        $beanJobId = new Job($v, '');
                        $job = ZhyxBeanstalkd::getInstance()->beansTalk->statsJob($beanJobId);
                        if ($job !== false) {
                            ZhyxBeanstalkd::getInstance()->beansTalk->delete($beanJobId);
                        }
                    }
                }
                return true;
            }
            return false;
        } catch (ServerException $serverException) {
            Log::error('任务已经消费:' . $serverException->getMessage());
        }

        return true;
    }


}